import { Options, Sequelize } from "sequelize";
import { ClsUtils } from "../utils/ClsUtils";
import { FileUtils } from "../utils/FileUtils";
import { PlusBase } from "./PlusBase";
import { ModelBase } from "./sequelize/ModelBase";
import { ModelConfigBase } from "./sequelize/ModelConfigBase";
import { ISqlConfig } from "../entity/IConfig";
const sequelize = require('sequelize');
export class PlusSequelize extends PlusBase {
    static NAME: string = "sequelize";
    protected _sequelize: Sequelize;
    protected mModelHash: any;
    protected mModelPath: string;
    private _option: Options;
    constructor(config: ISqlConfig, modelPath?: string, options?: Options, name?:string) {
        super(name || PlusSequelize.NAME);
        var defaultOpt = {
            host: "127.0.0.1",
            dialect: "mysql",
            port: 3306,
            pool: {
                max: 300,
                min: 1,
                idle: 20000,
                acquire: 20000,
                evict: 30000,
                handleDisconnects: true,
                connectRetries: 3,
                acquireTimeoutMillis: 0
            },
            timezone: "+08:00",
            benchmark: true,
            logging: this._onLog.bind(this)
        }
        this._option = Object.assign({}, defaultOpt, config.options, options);
        this.mModelPath = modelPath;
        this._sequelize = new sequelize(config.dbName, config.user, config.pwd, this._option);
    }

    async init() {
        this.mModelHash = {};
        await this.mInitModelPath();
        this.mCompleteHandle();
    }

    protected async mInitModelPath() {
        let jsFiles = FileUtils.readFile(this.mModelPath, "js");
        for (let i = 0; i < jsFiles.length; i++) {
            let fileName = jsFiles[i];
            var d = require(this.mModelPath + "//" + fileName);
            if (d) {
                let configCls: typeof ModelConfigBase;
                let modelCls: typeof ModelBase;
                for (var key in d) {
                    if (ClsUtils.targetIsCls(d[key], ModelConfigBase)) {
                        configCls = d[key];
                    } else if (ClsUtils.targetIsCls(d[key], ModelBase)) {
                        modelCls = d[key];
                    }
                }
                if (configCls && modelCls) {
                    let config = new configCls(configCls.TABLE_NAME);
                    if (config.name) {
                        await this.initModel(config, modelCls);
                    }
                }
            }
        }
    }

    public async initModel(config: ModelConfigBase, modelCls: typeof ModelBase) {
        config.init();
        let options = config.options;
        options.sequelize = this._sequelize;
        modelCls.init(config.attr, options);
        this.mModelHash[modelCls.tableName] = modelCls;
        if (config.needSync) {
            await modelCls.sync({alter:config.alter});
        }
    }


    public defineModel<T, T1>(config: ModelConfigBase) {
        config.init();
        let model = this._sequelize.define(config.name, config.attr, config.options);
        return model;
    }

    public getModel(name: string) {
        return <any>this._sequelize.model(name);
    }


    protected mGetValues() {
        var arr = this["_options"].attributes;
        var obj = {};
        if (arr) {
            for (var i = 0; i < arr.length; i++) {
                obj[arr[i]] = this[arr[i]];
            }
        } else {
            for (var key in this["dataValues"]) {
                obj[key] = this[key];
            }
        }
        return obj;
    }

    /**
    * 执行语句输出
    * @param sql  sql语句
    * @param time 执行耗时
    */
    public _onLog(sql: string, time: number) {
        this.emit("record", { sql_text: sql.replace('Executed (default):', ''), cost_time: time });
    }
}